Proyecto grupal CA-0305

Preparación

Para medir los tiempos de ejecución se utilizará la librería microbenchmark, la cual permite medir el tiempo de una función múltiples veces.

Así, primeramente se leen varios scripts que contienen diferentes funciones y clases que se usarán a lo largo del proyecto.

# Scripts de R para la preparación
scripts_r <- c(
  "cod/r/librerias.R",
  "cod/r/fun_calcular_tiempos.R",
  "cod/r/fun_df_tiempos_simples.R",
  "cod/r/fun_df_prod_acum.R",
  "cod/r/fun_df_texto.R",
  "cod/r/fun_df_tiempos_dobles.R",
  "cod/r/fun_df_tiempos_graficos.R",
  "cod/r/fun_df_tiempos_elimcol.R",
  "cod/r/fun_df_tiempos_imputacion.R",
  "cod/r/fun_lectura_excel.R",
  "cod/r/fun_lectura_txt.R",
  "cod/r/fun_lectura_csv.R",
  "cod/r/fun_graficos_simples.R",
  "cod/r/grafico_doble_prodacum.R",
  "cod/r/grafico_doble_graficos.R",
  "cod/r/grafico_doble_texto.R",
  "cod/r/fun_graficos_dobles_limpieza.R",
  "cod/r/fun_graficos_dobles_elimcols.R",
  "cod/r/fun_graficos_dobles_agrupacion.R"
)

# Se leen los scripts
invisible(lapply(scripts_r, source))

# Scripts de Python para la preparación
scripts_python <- c(
  "cod/python/librerias.py",
  "cod/python/clases.py"
)

# Se leen los scripts
invisible(lapply(scripts_python, source_python))

Operaciones simples

Primeramente, se crea un objeto de la clase OperacionesBasicas que se usará a lo largo de esta sección.

# Crear el objeto de OperacionesBasicas

Operaciones matriciales

Inicialmente se intentaron usar librerías como Matrix, bigmemory, LAPLACK, BLAS, bigalgebra o expm, pero algunas de estas requieren de una instalación externa y más compleja que una librería común y corriente, además de que dichas librerías no optimizan la multiplicación elemento a elemento sino que optimizan el calculo matricial de matrices, o se especializan en otros tipos de procedimientos con matrices, lo cual no es lo que se quiere en este ejercicio.

Esto quiere decir que, por ejemplo, al hacer A %^% 2 o, lo que es lo mismo, A %*% A, se hace la multiplicación matricial de A con A y no la multiplicación elemento a elemento de A con A, que es lo deseado. También se intentó paralelizar el código usando las librerías doParallel y foreach, pero resultó ser bastante más ineficiente hacer esto que hacerlo por defecto.

Así, se decidió que la mejor manera de hacer este ejercicio es hacerlo con lo que trae R por defecto; es decir, crear una matriz con matrix() y utilizar el operador ^ para elevar a la 100 elemento a elemento, así como * y + para multiplicarla por 10 y sumarle 5, respectivamente.

Si bien el tiempo de ejecución puede parecer alto, es lo mejor que se pudo encontrar que está a nuestro alcance.

Así, se leen los scripts necesarios para este ejercicio.

# Scripts para las operaciones matriciales
scripts <- c(
  "cod/r/fun_creacion_matriz.R",
  "cod/r/fun_operacion_matricial.R",
  "cod/r/creacion_matriz.R"
)

# Se leen los scripts
invisible(lapply(scripts, source))

Se calculan los tiempos de ejecución del ejercicio.

calcular.tiempos(function()
  operacion.matricial(matriz))
## Unit: seconds
##   expr      min       lq     mean   median      uq      max neval
##  fun() 4.335901 4.377164 4.421122 4.396172 4.43934 4.635803    10

Se guardan los tiempos promedio por integrante de realizar la operación matricial.

# Se guarda este tiempo por integrante en un dataframe
tiempos.matrices <- df.tiempos.simples(c(4.246259, 5.99183, 7.257987, 7.87946))

Se grafica lo anterior.

graficos.simples(tiempos.matrices, "coral3", "Tiempos del ejercicio de matrices")

Se procede a hacer lo mismo en Python.

# Simular que se calculan los tiempos promedio de ejecución
# Guardar esos tiempos en un Dataframe
# Graficar los tiempos

Producto acumulado

Scripts necesarios para el ejercicio.

# Scripts para el producto acumulado
scripts <- c(
  "cod/r/fun_producto_acumulado_cumprod.R",
  "cod/r/fun_producto_acumulado_sapply.R"
)

# Se leen los scripts
invisible(lapply(scripts, source))

Se calculan los tiempos de ejecución del ejercicio tanto con cumprod como con sapply.

calcular.tiempos(function()
  prod.acum.cumprod(runif(
    100000, min = .Machine$double.eps, max = 2
  )))
## Unit: seconds
##   expr       min        lq       mean     median        uq       max neval
##  fun() 0.0055224 0.0056561 0.00593815 0.00568445 0.0058734 0.0078974    10
calcular.tiempos(function()
  prod.acum.sapply(runif(
    100000, min = .Machine$double.eps, max = 2
  )))
## Unit: seconds
##   expr      min       lq     mean   median       uq      max neval
##  fun() 32.48389 34.21714 37.96342 39.82535 40.83107 41.68956    10

Se guardan los tiempos promedio por integrante de realizar el producto acumulado.

tiempos.producto.acumulado <- df.tiempos.prodacum(
  c(0.001, 0.01, 0.006, 0.02),
  c(27.93, 44.54, 27.82, 115.22),
  c("Santiago", "Paula", "Eyeri", "Alejandro")
)

Se grafica lo anterior.

graficos.dobles.prodacum(
  tiempos.producto.acumulado,
  "Tiempos del producto acumulado",
  c(
    "cumprod" = "seagreen3",
    "sapply" = "turquoise3"
  )
)

Cifrado César

Scripts necesarios para el ejercicio.

# Scripts para el cifrado César
scripts <- c(
  "cod/r/descarga_el_principito.R",
  "cod/r/cifrado_cesar_chartr.R",
  "cod/r/cifrado_cesar_stringi.R"
)

# Se leen los scripts
invisible(lapply(scripts, source))

Se calculan los tiempos de ejecución del ejercicio tanto con chartr como con stringi.

calcular.tiempos(function()
  cifrado.chartr(principito, 5))
## Unit: seconds
##   expr       min        lq       mean    median        uq      max neval
##  fun() 0.0115916 0.0116463 0.01241343 0.0118035 0.0119627 0.018011    10
calcular.tiempos(function()
  cifrado.stringi(principito, 5))
## Unit: seconds
##   expr      min      lq   mean   median       uq      max neval
##  fun() 1.183664 1.19722 1.2741 1.289747 1.336426 1.377882    10

Se guardan los tiempos promedio por integrante de realizar el Cifrado César

tiempos.texto <- df.tiempos.texto(
  c(0.012, 0.02, 0.016, 0.044),
  c(1.25, 2.46  , 1.3, 3.44),
  c("Santiago", "Paula", "Eyeri", "Alejandro")
)

Se grafica lo anterior.

graficos.dobles.texto(
  tiempos.texto,
  "Tiempos del cifrado César",
  c(
    "chartr" = "aquamarine3",
    "stringi" = "mediumpurple3"
  )
)

Trabajo con bases de datos

Para esta sección, es importante mencionar que en varios ejercicios se modifica la base de datos en cuestión a un objeto de tipo data.table, este se trata de una versión más eficiente de los data.frames que R trae por defecto. Además, es necesario mencionar que los data.table tienen una sintaxis similar a SQL.

Limpieza eficiente

Inicialmente, se procede a descargar la base de datos a utilizar para generar los gráfico, para esto se pondrá en formato data.table, este se trata de una versión más eficiente de los data.frames que R trae por defecto, es necesario mencionar que los data.table tienen una sintaxis similar a SQL.

Los data.table tienen un operador muy importante que es el :=, lo cual mejora en gran medida la eficiencia de muchas operciones.

Además, se usará chartr para modificar los caracteres únicos requeridos, pues esta función está optimizada para ese proceso.

De esta manera, se proceden a leer los scripts necesarios para este ejercicio.

# Scripts para la limpieza eficiente
scripts <- c(
  "cod/r/descarga_muertes_cr.R",
  "cod/r/fun_limpieza_df.R",
  "cod/r/fun_limpieza_dt.R"
)

# Se leen los scripts
invisible(lapply(scripts, source))

Se calculan los tiempos de ejecución del ejercicio tanto con DataFrame como con DataTable.

calcular.tiempos(function()
  limpieza.df(muertes.original))
## Unit: seconds
##   expr      min       lq     mean   median       uq      max neval
##  fun() 4.002705 4.010127 4.075639 4.026086 4.131551 4.274546    10
calcular.tiempos(function()
  limpieza.dt(muertes.original))
## Unit: seconds
##   expr      min       lq     mean   median       uq      max neval
##  fun() 3.430143 3.492168 3.560935 3.542375 3.648219 3.723472    10

Se guardan los resultados en un DataFrame.

tiempos.limpieza <-
  df.tiempos.limpieza(
    c(3.05496, 7.309475, 4.02244, 8.529539),
    c(4.77649, 11.26839, 5.891517, 11.31008),
    integrantes = c("Santiago", "Paula", "Eyeri", "Alejandro")
  )

Se grafican los tiempos de la limpieza.

graficos.dobles.limpieza(
  tiempos.limpieza,
  "Tiempos de limpieza de una base",
  c(
    "Con datatable" = "steelblue2",
    "Sin datatable" = "brown3"
  )
)

Gráficos en paralelo

Para este ejercicio, primero se realiza la limpieza de la base de datos ‘muertes.original’. Posteriormente, se procede a realizar el ejercicio de los gráficos. Para esto se usó el paquete furrr, este permite realizar ciclos en paralelo, lo cual se puede observar que aumenta en gran medida la eficiencia. Se realizan gráficos de barras para las categóricas e histogramas para las numéricas.

Para empezar, se leen los scripts necesarios para el ejercicio.

# Scripts para la limpieza eficiente
scripts <- c(
  "cod/r/limpieza_para_graficos.R",
  "cod/r/ordenar_rango_etario.R",
  "cod/r/fun_histograma.R",
  "cod/r/fun_barras.R",
  "cod/r/setup_graficos_paralelo.R"
)

# Se leen los scripts
invisible(lapply(scripts, source))

Con lo anterior, probamos el tiempo que tardan las funciones.

# Tomamos los tiempos 10 veces e imprimimos los resultados
(microbenchmark(
  future_map(numericos, ~ fun.histograma(muertes.df, .x)),
  future_map(categoricos, ~ fun.barras(muertes.df, .x)),
  times = 10
))
## Unit: seconds
##                                                    expr      min       lq
##  future_map(numericos, ~fun.histograma(muertes.df, .x)) 3.682164 3.743252
##    future_map(categoricos, ~fun.barras(muertes.df, .x)) 5.615737 6.081246
##      mean   median       uq      max neval cld
##  4.330771 3.964884 4.163975 6.513981    10  a 
##  6.272897 6.168077 6.327780 7.742537    10   b

Se realiza un DataFrame con el tiempo (en segundos) promedio de los histogramas y de los gráficos de barras, respectivamente.

tiempos.graficos <- df.tiempos.graficos(
  primeros.tiempos = c(8.46395, 4.682311, 8.531366, 10.83528),
  segundos.tiempos = c(13.31152, 7.107419, 14.893497, 16.36664)
)

Se grafica lo anterior.

graficos.dobles.graf(
  tiempos.graficos,
  "Tiempos de gráficos",
  c(
    "Histogramas" = "khaki3",
    "Gráficos de barras" = "green3"
  )
)

Media móvil

Para este ejercicio, se probará un método manual, pues las funciones existentes devuelven aún más nulos de los que hay en el vector original. Para realizar este proceso más rápido se probó a paralelizar el código, pero esto resultó más ineficiente que la versión final.

Primeramente, se leen los scripts necesarios para el ejercicio.

# Scripts para la media móvil
scripts <- c(
  "cod/r/setup_media_movil.R",
  "cod/r/fun_media_movil.R"
)

# Se leen los scripts
invisible(lapply(scripts, source))

Con lo anterior, se realizan 10 simulaciones para ver el tiempo promedio.

calcular.tiempos(function()
  media.movil(copia.col, 5))
## Unit: seconds
##   expr       min        lq       mean    median        uq       max neval
##  fun() 0.0028014 0.0028394 0.00365646 0.0029049 0.0031499 0.0097963    10

Se hace el DataFrame con los valores promedio de cada integrante.

tiempos.mediam <- df.tiempos.simples(c((3.15827) / 1000,
                                       (2.78863) / 1000,
                                       (10.75752) / 1000,
                                       (30.66207) / 1000
),
c("Santiago", "Eyeri", "Paula", "Alejandro"))

En un gráfico.

graficos.simples(tiempos.mediam,
                 "darkorchid2",
                 "Tiempos de imputación por media móvil")

Eliminación de columnas por porcentaje de valores nulos

Similar al ejercicio de media móvil, se inicia descargando la base de datos. Luego, se crea la función que hace lo deseado, la cual modifica el DataFrame a un objeto de tipo DataTable, y se mide el tiempo promedio de ejecución usando microbenchmark. Esta función ya hace lo requerido en un tiempo considerablemente rápido, por lo que si fuera posible mejorar el tiempo de ejecución no habría una diferencia significativa.

# Scripts para la media móvil
scripts <- c(
  "cod/r/setup_eliminar_nulos.R",
  "cod/r/fun_eliminar_cols_df.R",
  "cod/r/fun_eliminar_cols_dt.R"
)

# Se leen los scripts
invisible(lapply(scripts, source))

Prueba de las funciones.

calcular.tiempos(function()
  eliminar.columnas.df(base.salarios, 0.01))
## Unit: seconds
##   expr       min        lq       mean    median        uq       max neval
##  fun() 0.0007266 0.0007285 0.00145605 0.0007815 0.0009072 0.0068014    10
calcular.tiempos(function()
  eliminar.columnas.dt(base.salarios, 0.01))
## Unit: seconds
##   expr      min        lq       mean    median        uq       max neval
##  fun() 0.000471 0.0004754 0.00128654 0.0005238 0.0005918 0.0074863    10

Por último, se hace un DataFrame con los tiempos de ejecución de cada integrante.

tiempos.eliminar.columnas <-
  df.tiempos.elimcol(
    c(0.00170, 0.00181, 0.00510, 0.00397),
    c(0.00186, 0.00125, 0.01990, 0.00150),
    c("Eyeri", "Santiago", "Paula", "Alejandro")
  )

En gráfico.

graficos.dobles.elimcols(
  tiempos.eliminar.columnas,
  "Tiempo de eliminación de columnas",
  c(
    "Con datatable" = "indianred3",
    "Sin datatable" = "hotpink2"
  )
)

Imputación por agrupación

Para este ejercicio, se inicia realizando la imputación por agrupación de múltiples maneras, iniciando con data.table.

Primeramente, se leen los scripts para este ejercicio.

# Scripts para la imputación por agrupación
scripts <- c(
  "cod/r/setup_imputacion_agrupacion.R",
  "cod/r/fun_agrupacion_df.R",
  "cod/r/fun_agrupacion_dt.R"
)

# Se leen los scripts
invisible(lapply(scripts, source))

Se prueban las funciones.

calcular.tiempos(function()
  agrupacion.df(base.salarios))
## Unit: seconds
##   expr       min        lq       mean     median       uq       max neval
##  fun() 0.0062892 0.0063431 0.00727658 0.00646825 0.007402 0.0105662    10
calcular.tiempos(function()
  agrupacion.dt(base.salarios))
## Unit: seconds
##   expr       min        lq      mean    median        uq       max neval
##  fun() 0.0024192 0.0025271 0.0036164 0.0032753 0.0040832 0.0074236    10

Se guardan los resultados en un DataFrame.

tiempos.agrupacion <- df.tiempos.imputacion(
  primeros.tiempos = c((3.04735) / 1000,
                       (4.2577) / 1000,
                       (5.95827) / 1000 ,
                       (8.804151) / 1000
  ),
  segundos.tiempos = c((7.28283) / 1000,
                       (9.236) / 1000,
                       (15.76346) / 1000 ,
                       (21.437711) / 1000
  ),
  integrantes = c("Santiago", "Eyeri", "Paula", "Alejandro")
)

Con un gráfico.

graficos.dobles.imputacion(
  tiempos.agrupacion,
  "Tiempos de gráficos",
  c(
    "Datatable" = "darkslategray3",
    "Dplyr" = "firebrick3"
  )
)

Aplicaciones prácticas

Regresión logística

Para este ejercicio, se hace la prueba correspondiente del modelo, para esto, se buscó sobre cómo separar los datos de un DataFrame.

Se leen los scripts para este ejercicio.

# Scripts para la regresión logística
scripts <- c(
  "cod/r/setup_reg_log.R",
  "cod/r/fun_optim.R",
  "cod/r/fun_reg_log.R",
  "cod/r/prueba_reg_log.R"
)

# Se leen los scripts
invisible(lapply(scripts, source))

Se hacen las pruebas de tiempo.

calcular.tiempos(
  function()
    regresion.logistica(
      X.train = X.train,
      y.train = y.train,
      X.val = X.val,
      y.val = y.val,
      num.iterations = 1000,
      learning.rate = 0.003
    )
)
## Unit: seconds
##   expr       min        lq      mean     median        uq       max neval
##  fun() 0.0604532 0.0700897 0.0759834 0.07425765 0.0850071 0.0869952    10

Tiempos de la regresión logística.

tiempos.regresion.log <-
  df.tiempos.simples(c(0.08, 0.06, 0.17, 0.23),
                     c("Eyeri", "Santiago", "Paula", "Alejandro"))

Gráfico de los tiempos de la regresión logística.

graficos.simples(tiempos.regresion.log,
                 "deepskyblue2",
                 "Tiempos de la regresión logística")

Modelo estocástico

Se empieza leyendo los scripts para el ejercicio.

# Scripts para el modelo estocástico
scripts <- c(
  "cod/r/setup_modelo_estoc.R",
  "cod/r/fun_primer_falso.R",
  "cod/r/primera_iteracion_modelo_estoc.R",
  "cod/r/fun_iteraciones_estoc.R",
  "cod/r/tiempo_modelo_estocastico.R"
)

# Se leen los scripts
invisible(lapply(scripts, source))
## Time difference of 0.02126718 mins

Se crea un DataFrame con los tiempos del modelo.

tiempos.estocast <- df.tiempos.simples(c(0.032, 0.046, 0.064, 0.124),
                                       c("Eyeri", "Santiago", "Paula", "Alejandro"))

Gráfico del modelo estocástico.

graficos.simples(tiempos.estocast, "pink2", "Tiempos del modelo estocástico")